/* * Copyright (c) 2004-2007 by Michael Connor. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of FormLayoutBuilder or Michael Connor nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.mlc.swing.layout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.dnd.Autoscroll; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceContext; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JTable; import com.jgoodies.forms.layout.CellConstraints; /** * I'm creating a table subclass to make it easier to handle dragging and * dropping * http://www.hut.fi/~landerso/cccp/src/java/cccp/mappingtool/util/MTTable.java */ @SuppressWarnings("serial") class DnDTable extends JTable implements DragSourceListener, DragGestureListener, DropTargetListener, Autoscroll { protected DragSource fDragSource = null; protected DropTarget fDropTarget = null; protected Component dragComponent = null; final static int AUTOSCROLL_INSET_SIZE = 20; final static int SCROLL_AMOUNT = 10; FormEditor parent; MultiContainerFrame superparent; public DnDTable(MultiContainerFrame granddaddy, FormEditor daddy) { super(); parent = daddy; superparent = granddaddy; fDragSource = new DragSource(); fDropTarget = new DropTarget(this, this); fDragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this); } /** * Drag and drop is kind of weird in that when you drag something, you often * start in a cell that has a component in it but by the time the drag * handling code get's the event the selection has moved into a cell that * does have a component in it and the drag fails. */ public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) { super.changeSelection(rowIndex, columnIndex, toggle, extend); Component component = getSelectedControl(); parent.setFormComponent(component); if (component != null) { // let's update the list control so when they select // something in the grid it becomes selected in the // list and the property values susequently change. parent.componentList.setSelectedValue(component, true); } else { ; } } /** * Implements autoscrolling. */ public Insets getAutoscrollInsets() { Rectangle visible = getVisibleRect(); Dimension size = getSize(); int top = 0, left = 0, bottom = 0, right = 0; if (visible.y > 0) { top = visible.y + AUTOSCROLL_INSET_SIZE; } if (visible.x > 0) { left = visible.x + AUTOSCROLL_INSET_SIZE; } if (visible.y + visible.height < size.height) { bottom = size.height - visible.y - visible.height + AUTOSCROLL_INSET_SIZE; } if (visible.x + visible.width < size.width) { right = size.width - visible.x - visible.width + AUTOSCROLL_INSET_SIZE; } return new Insets(top, left, bottom, right); } /** * Implements autoscrolling. */ public void autoscroll(Point cursorLocn) { Rectangle visible = getVisibleRect(); int x = 0, y = 0, width = 0, height = 0; // Scroll left. if (cursorLocn.x < visible.x + AUTOSCROLL_INSET_SIZE) { x = -SCROLL_AMOUNT; width = SCROLL_AMOUNT; } // Scroll right. else if (cursorLocn.x > visible.x + visible.width - AUTOSCROLL_INSET_SIZE) { x = visible.width + SCROLL_AMOUNT; width = SCROLL_AMOUNT; } // Scroll up. if (cursorLocn.y < visible.y + AUTOSCROLL_INSET_SIZE) { y = -SCROLL_AMOUNT; height = SCROLL_AMOUNT; } // Scroll down. else if (cursorLocn.y > visible.y + visible.height - AUTOSCROLL_INSET_SIZE) { y = visible.height + SCROLL_AMOUNT; height = SCROLL_AMOUNT; } ((JComponent) getParent()).scrollRectToVisible(new Rectangle(x, y, width, height)); } public void dragEnter(DragSourceDragEvent event) { } public void dragOver(DragSourceDragEvent event) { DragSourceContext context = event.getDragSourceContext(); java.awt.Point location = event.getLocation(); Point org = this.getLocationOnScreen(); Point relLoc = new Point(location.x - org.x, location.y - org.y); int col = columnAtPoint(relLoc); int row = rowAtPoint(relLoc); Component component = getControlAt(col, row); if (col < 1 || row < 1 || component != null) { context.setCursor(DragSource.DefaultMoveNoDrop); } else { context.setCursor(DragSource.DefaultMoveDrop); } } public void dropActionChanged(DragSourceDragEvent event) { } public void dragExit(DragSourceEvent event) { } public void dragDropEnd(DragSourceDropEvent event) { } public void dropActionChanged(DropTargetDragEvent event) { } public void dragExit(DropTargetEvent event) { } public void dragGestureRecognized(DragGestureEvent event) { Point p = event.getDragOrigin(); int row = rowAtPoint(p); int col = columnAtPoint(p); Component component = getControlAt(col, row); if (component != null) { event.startDrag(java.awt.dnd.DragSource.DefaultMoveDrop, new TransferableWrapper(component), this); } } public void dragEnter(DropTargetDragEvent dropTargetDragEvent) { try { if (dropTargetDragEvent.isDataFlavorSupported(new DataFlavor( DataFlavor.javaJVMLocalObjectMimeType))) { dropTargetDragEvent.acceptDrag(DnDConstants.ACTION_MOVE); } else { dropTargetDragEvent.rejectDrag(); } } catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); } } public void dragOver(java.awt.dnd.DropTargetDragEvent dropTargetDragEvent) { //DropTargetContext context = dropTargetDragEvent.getDropTargetContext(); try { //java.awt.Point location = dropTargetDragEvent.getLocation(); //int col = columnAtPoint(location); //int row = rowAtPoint(location); if (dropTargetDragEvent.isDataFlavorSupported(new DataFlavor( DataFlavor.javaJVMLocalObjectMimeType))) { dropTargetDragEvent.acceptDrag(DnDConstants.ACTION_MOVE); } else { dropTargetDragEvent.rejectDrag(); } } catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); } } public void drop(java.awt.dnd.DropTargetDropEvent e) { CellConstraints componentConstraints = null; Component component = null; try { java.awt.Point location = e.getLocation(); int col = columnAtPoint(location); int row = rowAtPoint(location); Component existComp = getControlAt(col, row); if (col < 1 || row < 1 || existComp != null) { // don't allow drop onto constraints row/col e.rejectDrop(); e.getDropTargetContext().dropComplete(true); return; } DataFlavor javaObject = new DataFlavor( DataFlavor.javaJVMLocalObjectMimeType); if (!e.isDataFlavorSupported(javaObject)) { // flavor unsupported e.rejectDrop(); return; } { e.acceptDrop(DnDConstants.ACTION_MOVE); Transferable transferable = e.getTransferable(); Object dropObject = transferable.getTransferData(javaObject); if (dropObject instanceof ComponentDef) { ComponentDef componentDef = (ComponentDef) dropObject; NewComponentDialog dlg = NewComponentDialog.doDialog( (JFrame) superparent, componentDef); if (dlg.succeeded()) { String componentName = dlg.getComponentName(); componentDef = dlg.componentDef; // insure the component name doesn't collide int suffix = 1; while (parent.containerLayout .getComponentByName(componentName) != null) { componentName = dlg.getComponentName() + "_" + suffix; suffix++; } componentDef.name = componentName; // store new name for later edit try { component = dlg.getInstance(); componentConstraints = new CellConstraints(col, row); // Earlier attempts to use beaninfo wasn't working for JPanel, // and did identify the JGoodies buttonbar as a container (which // it is, but not for our purposes). boolean isContainer = componentDef.isContainer; // the best way to add this control is to setup the constraints // in the map of name->constraints and then add it to the // container. // this layout manager will intercept it, find the constraints // and then set it up properly in the table and assign the maps. parent.containerLayout.addComponent(componentName, componentDef, componentConstraints); parent.container.add(component, componentName); parent.newComponents.add(component); if (isContainer) { superparent.addContainer(componentName, (Container) component); } e.dropComplete(true); parent.updateList(); parent.updateLayout(component); // force preview relayout parent.updateLayouts(); // the key for updating with new panel changeSelection(componentConstraints.gridY, componentConstraints.gridX, false, false); repaint(); return; } catch (Throwable t) { t.printStackTrace(); // JOptionPane // .showMessageDialog(parent, // "Unable to create new instance of " // + componentClass.getName() + "(" + t.getMessage() // + ")", "Error", JOptionPane.ERROR_MESSAGE); e.dropComplete(false); return; } } } else if (dropObject instanceof Component) { component = (Component) dropObject; componentConstraints = parent .getComponentConstraints(component); if (col > 0 && row > 0) { componentConstraints.gridX = col; componentConstraints.gridY = row; componentConstraints.gridWidth = Math.min( componentConstraints.gridWidth, parent.containerLayout.getColumnCount() - componentConstraints.gridX + 1); componentConstraints.gridHeight = Math.min( componentConstraints.gridHeight, parent.containerLayout.getRowCount() - componentConstraints.gridY + 1); if (!component.isVisible()) { component.setVisible(true); } parent.topComponent = component; // make sure this sorts to the top... e.dropComplete(true); // we either moved a component or added it to the // container for the first time so either way we // need to update the layout... parent.updateLayout(component); changeSelection(componentConstraints.gridY, componentConstraints.gridX, false, false); // we repaint the table because it has changed. i would think that // firing a table update event would be more appropriate but this seems // to work just fine. repaint(); // repaint the list because we change the component text from bold // to plain once it's been added. parent.updateList(); return; } } else { // someone dropped something unexpected on us... e.dropComplete(false); return; } } } catch (Exception exception) { exception.printStackTrace(); // flavor exception or ClassNotFoundException e.rejectDrop(); return; } } public Component getControlAt(int col, int row) { Component component = (row == 0 || col == 0) ? null : (Component) getModel().getValueAt(row, col); return component; } public Component getSelectedControl() { int col = getSelectedColumn(); int row = getSelectedRow(); return getControlAt(col, row); } }